#include <iostream>
#include <cstdlib>

using namespace std;

/*
    析构函数:
    ·C++中的类可以定义一个特殊的成员函数清理对象，这个特殊的成员函数叫做析构函数
      ·语法：~ClassName()
    ·析构函数没有参数也没有任何返回类型的声明
    ·析构函数在对象销毁时自动被调用
    ·析构函数调用机制
      ·C++编译器自动调用
 */
int main() {
    class Animal {
    public:
        //使用场景:比如成员变量中存在指针
        int *p;
        int *p2;
        char *name;

        //构造函数
        Animal() {
            //构造函数中在堆上为指针分配空间
            p = (int *) malloc(40);
            //new运算符动态分配堆内存
            // 语法: 指针变量=new 数据类型(初始化值); 指针变量=new 数据类型[表达式];
            // 作用: 从堆分配一块对应数据类型大小的存储空间，返回首地址
            // TIPS: 初始化值可缺省。创建数组对象时，不能为对象指定初始值。
            p2 = new int(10);//在堆上申请一个int类型大小的空间（4Bytes）,并且将申请的堆空间的内容初始化为10
            name = new char[32];
            cout << "Animal的构造函数被调用" << endl;
        }

        Animal(int *p) {
            p2 = p;
            cout << "Animal的带参构造函数被调用" << endl;
        }

        //析构函数
        ~Animal() {
            //我们在构造函数中为指针变量p在堆上申请了一段空间，
            // 因为堆上的空间需要手动申请手动释放，所以对象在销毁时，我们还需要手动释放堆上的空间，我们可以将释放堆空间的操作放在析构函数中！
            free(p);
            delete p2;
            //delete运算符释放已分配的内存空间
            // 语法: delete 指针变量;  delete[] 指针变量;
            // TIPS:指针变量是一个通过new返回的指针
            delete[] name;
            cout << "Animal的析构函数被调用" << endl;
        }
    };
    Animal animal;
    cout << *animal.p2 << endl;

    //在堆上申请4个int类型大小的空间（4*sizeof(int)==16Bytes） 4：数组的长度
    int *p2 = new int[4];
    for (int i = 0; i < 4; i++) {
        cout << "堆上分配的值(不确定，因为还没有初始化):" << *(p2 + i) << endl; //cout << p2[i] << endl;
    }
    delete[] p2;

    //在堆上申请一个Box类型大小的空间
    Animal *pAni = new Animal(); //调用构造函数
    delete pAni;
    //new和delete是运算符，不是函数，因此执行效率高。

    //在堆上申请四个Box类型大小的空间
    Animal *pArr = new Animal[3]; //调用无参的构造函数
    delete[] pArr;


    //在堆上申请一个Box类型大小的空间
    int *pInt = new int(10);
    Animal *p5 = new Animal(pInt); //调用带参数的构造函数
    cout << &pInt << ": " << pInt << ": " << *pInt << endl;
    delete pInt;//记住，释放空间只是告知操作系统这个堆空间已经可以重新分配给其他变量了
    cout << &pInt << ": " << pInt << ": " << *pInt << endl;//所以该变量目前依旧是指向那片空间的
    cout << p5->p2 << ": " << *p5->p2 << endl;
    pInt = new int(20);//甚至还可以继续使用变量，当然根据new的作用则会再次分配一次新的堆空间
    cout << &pInt << ": " << pInt << ": " << *pInt << endl;
    cout << p5->p2 << ": " << *(p5->p2) << endl;
    delete p5;


    cout << "malloc与new的不同" << endl;
    Animal *pAni2 = (Animal *) malloc(sizeof(Animal)); //不会调用构造函数！！
    free(pAni2); //不会调用析构函数
    //TIPS:虽然为了与C语言兼容，C++仍保留malloc和free函数，但建议用户不用malloc和free函数，而用new和delete运算符。

    /*
        总结:
        1、malloc/free为C的标准库函数，new、delete则为C++的操作运算符
        2、new能自动计算需要分配的内存空间，而malloc需要手工计算字节数
        3、new与delete直接带具体类型的指针，malloc和free返回void类型的指针。
        4、new类型是安全的，而malloc不是。例如int *p = new float[2];就会报错；
            而int  *p = malloc(2  * sizeof(float))编译时编译器就无法指出错误来。
        5、new调用构造函数，malloc不能；delete调用析构函数，而free不能
        6、new/delete是操作符可以重载，malloc/free则不能
     */


    //类中的成员变量为另外一个类的实例化对象时，构造函数与析构函数的调用顺序
    class ABC {
    public:
        ABC(int a, int b, int c) {
            cout << "ABC(int a, int b, int c)" << endl;
        }

        ~ABC() {
            cout << " ~ABC() " << endl;
        }

    private:
        int a;
        int b;
        int c;
    };

    class MyD {
    public:
        //成员对象所属的类中没有实现无参构造函数的时候，需要使用初始化成员列表
        MyD() : abc1(1, 2, 3), abc2(4, 5, 6) {
            cout << "MyD()" << endl;
        }

        ~MyD() {
            cout << "~MyD()" << endl;
        }

    private:
        //当类中的成员变量为另外一个类的实例化对象时，我们称这个对象为"成员对象"
        ABC abc1; //c++编译器不知道如何构造abc1
        ABC abc2;
    };
    MyD myD;
    /*
        1）当类中有成员变量是其它类的对象时，首先调用成员对象的构造函数，调用顺序与声明顺序相同；之后调用自身类的构造函数
        2）析构函数的调用顺序与对应的构造函数调用顺序相反
     */

    return 0;
}